﻿using System;
using System.Collections.Generic;

namespace Core
{
    public class Binder : Core.Component
    {
        private readonly Dictionary<Type, Dictionary<string, Binding>> dict = new();

        public void Awake() { }

        public override void Dispose()
        {
            if (IsDisposed) return;

            foreach (var pair1 in dict)
            {
                foreach (var pair2 in pair1.Value)
                {
                    pair2.Value.Dispose();
                }
                pair1.Value.Clear();
            }
            dict.Clear();

            base.Dispose();
        }

        public void Bind<T>(T obj)
        {
            Bind(string.Empty, obj);
        }

        public void Bind<T>(string name, T obj)
        {
            var t = typeof(T);
            if (!dict.ContainsKey(t))
            {
                dict[t] = new Dictionary<string, Binding>();
            }

            var dict2 = dict[t];
            if (!dict2.ContainsKey(name))
            {
                dict2[name] = G.Factory.Create<Binding>();
            }

            var binding = dict2[name];
            binding.Set(obj);
        }

        public void Unbind<T>()
        {
            Unbind<T>(string.Empty);
        }

        public void Unbind<T>(string name)
        {
            var t = typeof(T);
            if (!dict.ContainsKey(t))
            {
                return;
            }

            var dict2 = dict[t];
            if (!dict2.ContainsKey(name))
            {
                return;
            }

            var binding = dict2[name];
            binding.Dispose();
            dict2.Remove(name);

            if (dict2.Count == 0)
            {
                dict.Remove(t);
            }
        }

        public T Get<T>() => (T)Get(typeof(T), string.Empty);

        public T Get<T>(string name) => (T)Get(typeof(T), name);

        private object Get(Type t, string name)
        {
            if (!dict.ContainsKey(t))
            {
                return default;
            }
            var dict2 = dict[t];
            if (!dict2.ContainsKey(name))
            {
                return default;
            }
            return dict2[name].Source;
        }

        private Binding GetBinding(Type t, string name)
        {
            if (!dict.ContainsKey(t))
            {
                return default;
            }
            var dict2 = dict[t];
            if (!dict2.ContainsKey(name))
            {
                return default;
            }
            return dict2[name];
        }

        public void Inject<T>(T t)
            where T : Component
        {
            var cls = G.Reflect.GetReflectedCls(t.GetType());
            if (cls == null)
            {
                return;
            }

            log.w($"Binder.Inject t={t}");
            foreach (var f in cls.InjectFields)
            {
                var binding = GetBinding(f.FieldType, string.Empty);
                if (binding == null)
                {
                    binding = G.Factory.Create<Binding>();
                    if (!dict.ContainsKey(f.FieldType))
                    {
                        dict[f.FieldType] = new Dictionary<string, Binding>();
                    }
                    var dict2 = dict[f.FieldType];
                    if (!dict2.ContainsKey(string.Empty))
                    {
                        dict2[string.Empty] = binding;
                    }
                }

                f.SetValue(t, binding.Source);
                binding.Add(t, f);
            }
        }

    }
}
